#ifndef MEMORYPOOL_HPP
#define MEMORYPOOL_HPP
#include <functional>
#include "config.hpp"
#include <memory>
#include <mutex>
#include <vector>
namespace myh{
    namespace pool{
        template <typename T>
        class MemoryPool {
        private:
            struct Node {
                Node* next;
            };
            bool is_construct=false;
            Node* freeList = nullptr;
            size_t freeNodes = 0, blockSize;
            std::vector<std::unique_ptr<char[]>> blocks;
            mutable std::mutex mtx;
            void expand(size_t need=1) {
                size_t expandSize = std::max(need, blockSize);
                char* newBlock = new char[expandSize * sizeof(T)];
                blocks.emplace_back(newBlock);
                for(size_t i=0; i<expandSize; ++i){
                    Node* node = reinterpret_cast<Node*>(newBlock + i*sizeof(T));
                    node->next = freeList;
                    freeList = node;
                }
                freeNodes += expandSize;
            }
        public:
            MemoryPool(size_t poolSize=1,size_t blockSize_=4096){
                blockSize=blockSize_;
                for(int i=0;i<poolSize;++i) expand();
            }
            ~MemoryPool() {
                while(freeList) {
                    Node* next = freeList->next;
                    freeList = next;
                }
            }
            T* allocate(size_t n=1) {
                std::lock_guard<std::mutex> lock(mtx);
                while(freeNodes < n) expand(n - freeNodes);
                T* start=reinterpret_cast<T*>(freeList);
                freeList=freeList->next;
                freeNodes-=n;
                return start;
            }
            void deallocate(T* ptr) {
                std::lock_guard<std::mutex> lock(mtx);
                Node* node = reinterpret_cast<Node*>(ptr);
                node->next = freeList;
                freeList = node;
                freeNodes++;
            }
            void setBlockSize(size_t newSize) {
                std::lock_guard<std::mutex> lock(mtx);
                if(blocks.empty()) blockSize = newSize;
            }
            size_t getAllocatedCount() const {
                std::lock_guard<std::mutex> lock(mtx);
                return (blockSize * blocks.size()) - freeNodes;
            }
            size_t getFreeNodes() const {
                std::lock_guard<std::mutex> lock(mtx);
                return freeNodes;
            }
        };
        template<typename U>
        class Ptr {
            MemoryPool<U>* pool;
            U* ptr;
            bool is_constructed = false;
        public:
            template<typename... Args>
            explicit Ptr(MemoryPool<U>& p,Args&&... args) {
                pool = &p;
                ptr = pool->allocate();
                set(std::forward<Args>(args)...);
            }
            ~Ptr() {
                if(ptr) {
                    if(is_constructed) ptr->~U();
                    pool->deallocate(ptr);
                }
            }
            template<typename... Args>
            void set(Args&&... args) {
                if(is_constructed) ptr->~U();
                new(ptr) U(std::forward<Args>(args)...);
                is_constructed = true;
            }
            U* get() const noexcept {
                return ptr;
            }
            U* operator->() const noexcept { return ptr; }
            U& operator*() const noexcept { return *ptr; }
            Ptr(const Ptr&) = delete;
            Ptr& operator=(const Ptr&) = delete;
            Ptr(Ptr&& other) noexcept 
                : pool(other.pool), ptr(other.ptr) {
                other.ptr = nullptr;
            }
            Ptr& operator=(Ptr&& other) noexcept {
                if(this != &other) {
                    pool = other.pool;
                    ptr = other.ptr;
                    other.ptr = nullptr;
                }
                return *this;
            }
        };
        template <typename T>
        struct Allocator{
            typedef T value_type;
            typedef T* pointer;
            typedef const T* const_pointer;
            typedef T& reference;
            typedef const T& const_reference;
            typedef size_t size_type;
            typedef ptrdiff_t difference_type;
            MemoryPool<T>& pool;
            Allocator(MemoryPool<T>& p) : pool(p) {}
            template <typename U>
            Allocator(const Allocator<U>& other) : pool(other.pool) {}
            T* allocate(size_t n) {
                return pool.allocate(n);
            }
            void deallocate(T* p, size_t) {
                pool.deallocate(p);
            }
        };
    }
    template<class T>
    using PAlloc = pool::Allocator<T>;
    template<class T>
    using PoolVector = std::vector<T,PAlloc<T>>;

}
#endif